package org.bukkit.pm.dmatch;
import it.patamau.bukkit.MySQLConnection;

import java.util.logging.Level;

import org.bukkit.ChatColor;
import org.bukkit.entity.Player;


/**
 * This class is used to map player status in the DM environment
 * to generate rankings and player oriented feedback
 * @author patamau
 *
 */
public class DMatchPlayer implements Comparable<DMatchPlayer> {
	
	public static final int MAX_NAME_LEN = 16, MAX_NUM_LEN = 5;
	
	private static String getFillSpaces(int siz){
		StringBuffer sb = new StringBuffer();
		for(int i=0; i<siz; i++){
			sb.append(" ");
		}
		return sb.toString();
	}
	
	public static String normalize(String s, int siz){
		if(s.length()<siz){
			return s+getFillSpaces(siz-s.length()); //fill
		} else if (s.length()>siz){
			return s.substring(0, siz); //truncate
		} else{
			return s; //as is
		}
	}

	private String 
		_name;
	private int 
		_deaths, // deaths number
		_kills; // kills number
	private long 
		_lastDead; //used to calculate dead timeout, based on current time millis
	private DMatchPlugin
		_plugin; //used to retrieve Player instances and such
	private HealthMonitorThread
		_hmthread = null;
	private DMatchPlayer 
		_lastKiller; //the recorded killer (will only last for a single hm timeout
	
	private class HealthMonitorThread extends Thread{
		public boolean _stop;
		public void run(){
			Player p;
			//_plugin.log(Level.INFO, _name+" health monitor started");
			_stop=false;
			while(!_stop){
				synchronized(_hmthread){
					try {
						_hmthread.wait();
					} catch (InterruptedException e) {
						//thread interrupted!?
						//_plugin.log(Level.INFO, _name+" health monitor interrupted while waiting");
						continue;
					}
				}
				//retrieve player every time health is checked
				p = _plugin.getServer().getPlayer(_name);
				if(p==null){
					//_plugin.log(Level.INFO, _name+" is not associated to a player instance (anymore)");
					continue;
				}
				//_plugin.log(Level.INFO, _name+" checking health (1): "+p.getHealth());
				if(p.getHealth()<=0){
					setDead(_lastKiller);
					_plugin.setModified(true);
					continue;
				}
				try {
					Thread.sleep(_plugin.getHMTimeout());
				} catch (InterruptedException e) {
					//thread interrupted?
					//_plugin.log(Level.INFO, _name+" health monitor interrupted while sleeping");
					continue;
				}
				//_plugin.log(Level.INFO, _name+" checking health (2): "+p.getHealth());
				if(p.getHealth()<=0){
					setDead(_lastKiller);
					_plugin.setModified(true);
				}
			}
			//_plugin.log(Level.INFO, _name+" health monitor terminated");
		}
	}
	
	public DMatchPlayer(String n, int k, int d, DMatchPlugin p){
		_name = n;
		_deaths = d;
		_kills = k;
		_lastDead = -1;
		_plugin = p;
		//hmthread will be started when the linked player actually joins
	}
	
	public String getName(){
		return _name;
	}
	
	public int getDeaths(){
		return _deaths;
	}
	
	public int getKills(){
		return _kills;
	}
	
	public boolean isOnline(){
		//use hmthread to see if the player is online or not
		return _hmthread!=null;
	}
	
	public void sendMessage(String msg){
		_plugin.sendDMMessage(_plugin.getServer().getPlayer(_name),msg);
	}
	
	/**
	 * If the DEAD_TIMEOUT passed, 
	 * increase the death number of this DMPlayer
	 * and the kills number of the killer DMPlayer (if any has been specified)
	 */
	private synchronized boolean setDead(DMatchPlayer killer){
		if(_lastDead<0 //initialized dead timeout: always true
			|| (System.currentTimeMillis()-_lastDead)>_plugin.getDTimeout()){ //check timeout
			_lastDead = System.currentTimeMillis();
			_deaths++;
			MySQLConnection conn = _plugin.getConnection();
			if(conn.isConnected()){
				//TODO: set property: dm.mysql.death
				conn.execute(""); //TODO: fill in the user code for mysql
			}
			if(killer!=null){
				killer._kills++;
				this.sendMessage("You've been killed by "+ChatColor.WHITE+killer.getName());
				killer.sendMessage("You killed "+ChatColor.WHITE+_name);
				if(_plugin.getBroadcast()){
					_plugin.broadcast(ChatColor.WHITE+_name+ChatColor.RED+" has been killed by "+killer.getName(), _name, killer.getName());
				}
			}else{
				this.sendMessage("You died");
				if(_plugin.getBroadcast()){
					_plugin.broadcast(ChatColor.WHITE+_name+ChatColor.RED+" died",_name,null);
				}
			}
			return true;
		}else{
			return false;
		}
	}
	
	public String toString(){
		StringBuffer sb = new StringBuffer();
		if(isOnline()){ //check online color
			sb.append(ChatColor.WHITE);
		}else{
			sb.append(ChatColor.DARK_GRAY);
		}
		sb.append(normalize(_name, MAX_NAME_LEN)); //name
		sb.append(" ");
		sb.append(ChatColor.RED);
		sb.append(normalize(Integer.toString(_kills), MAX_NUM_LEN)); //kills
		sb.append(ChatColor.YELLOW);
		sb.append(normalize("/",MAX_NUM_LEN)); //separator
		sb.append(ChatColor.DARK_RED);
		sb.append(normalize(Integer.toString(_deaths), MAX_NUM_LEN)); //deaths
		return sb.toString();
	}
	
	public String serialize(){
		return _name+":"+_kills+":"+_deaths+":";
	}
	
	public static DMatchPlayer parse(String serialized, DMatchPlugin plugin){
		String[] split = serialized.split(":");
		StringBuffer sb = new StringBuffer();
		for(String s: split){
			sb.append(s);
			sb.append(" ");
		}
		plugin.log(Level.INFO,"loading "+sb.toString());
		//check serialization data
		if(split.length<3){
			plugin.log(Level.WARNING, "wrong DM player format");
			return null;
		}
		String n = split[0];
		int k, d;
		try{
			k = Integer.parseInt(split[1]);
			d = Integer.parseInt(split[2]);
		}catch(NumberFormatException e){
			plugin.log(Level.WARNING, "cannot initialize DM player: not valid stats for '"+n+"'");
			return null;
		}
		return new DMatchPlayer(n,k,d,plugin);
	}
	
	/**
	 * Compare player names
	 */
	public boolean equals(Object o){
		if(o instanceof DMatchPlayer){
			return ((DMatchPlayer)o)._name==_name;
		}
		return false;
	}

	public int compareTo(DMatchPlayer o) {
		if(o instanceof DMatchPlayer){
			DMatchPlayer pp = (DMatchPlayer)o;
			if(pp._kills>_kills){
				return 1;
			}else if(pp._kills<_kills){
				return -1;
			}else //pp kills == kills
				if(pp._deaths>_deaths){
					return -1;
				}else if(pp._deaths<_deaths){
					return 1;
				}else //pp deaths = deaths
					return 0;
		}
		return 0;
	}
	
	/**
	 * Damage by someone
	 * @param killer
	 */
	public void checkHealth(DMatchPlayer killer){
		_lastKiller = killer;
		checkHealth();
	}
	
	/**
	 * Damaged by environment
	 */
	public void checkHealth(){
		if(_hmthread==null) return;
		synchronized(_hmthread){
			_hmthread.notify();
		}
	}
	
	public void startHMonitor(){
		_hmthread = new HealthMonitorThread();
		_hmthread.start();
	}
	
	public void stopHMonitor(){
		if(_hmthread==null) return;
		_hmthread._stop=true;
		synchronized(_hmthread){
			_hmthread.notify();
		}
		_hmthread=null;
	}
}
